/*
 * auipopupwindow.cpp
 *
 *  Created on: 2012-7-24
 *      Author: Yangde
 */

#include "wx/wxprec.h"
#include "wx/window.h"
#include "wx/dcbuffer.h"
#include "wx/msgdlg.h"
#include "wx/display.h"
#include "wx/frame.h"


#include "auiutils.h"
#include "auimenu.h"
#include "auipopupwindow.h"

BEGIN_EVENT_TABLE(auiPopupWindow, wxPopupWindow)
    EVT_LEFT_DOWN(auiPopupWindow::OnLeftDown)
    EVT_LEFT_UP(auiPopupWindow::OnLeftUp)

    EVT_RIGHT_DOWN(auiPopupWindow::OnRightDown)
    EVT_MOTION(auiPopupWindow::OnMouseMove)
    EVT_PAINT(auiPopupWindow::OnPaint)
    EVT_MOUSE_CAPTURE_LOST(auiPopupWindow::OnCaptureLost)
END_EVENT_TABLE()

auiPopupWindow::auiPopupWindow(wxWindow * parent, auiMenu* menu)
 {
	(void) Create(parent, wxBORDER_NONE);
    m_menu = menu;
    m_menu->UnSelectItem();
	t_margin=2;
	b_margin=2;
	l_margin=3;
	r_margin=3;

	t_padding = b_padding = 4;
	l_padding = 23;
	r_padding = 20;
	separator_height = 7;
	calcClientSize();
	SetFont(*wxNORMAL_FONT);
	SetBackgroundStyle(wxBG_STYLE_CUSTOM);

	oldMousePos.x = -100;
	oldMousePos.y = -100;
}

auiPopupWindow::~auiPopupWindow() {

}

void auiPopupWindow::calcClientSize()
{

	wxClientDC *dc= new wxClientDC(this);
	dc->SetFont(*wxNORMAL_FONT);
	int x,y;
	dc->GetTextExtent(wxT("ABCDEFGjf"),&x,&y);
	font_height = y;
	menu_item_hight = t_padding+font_height+b_padding;
	menu_item_width = 0;
	for (int i=0;i<m_menu->GetItemCnt();i++)
	{
		if(m_menu->GetItemType(i) != auiItemSeparator)
			dc->GetTextExtent(m_menu->GetItemText(i),&x,&y);
		if(x>menu_item_width)
			menu_item_width = x;
	}

	if(dc)
	{
		delete dc;
	}

	int height = t_margin+b_margin+2/*border*/;
    int width = l_margin+l_padding+menu_item_width+r_padding+r_margin+2/*border*/;

	for (int i=0;i<m_menu->GetItemCnt();i++)
	{
		if(m_menu->GetItemType(i) == auiItemSeparator)
			height += separator_height;
		else
			height += menu_item_hight;
	}
	SetClientSize(width,height);

}


void auiPopupWindow::OnPaint(wxPaintEvent& evt)
{
	wxAutoBufferedPaintDC dc(this);
	wxRect cli_rect(wxPoint(0, 0), GetClientSize());
	dc.SetPen(wxPen(wxAuiUtils::GetInstance()->m_base_colour.ChangeLightness(70)));
	dc.SetBrush(*wxWHITE_BRUSH);
	dc.DrawRectangle(cli_rect);
	dc.SetFont(*wxNORMAL_FONT);

	wxRect rc = wxRect(1+l_margin,
			           1+t_margin,
			           l_padding+menu_item_width+r_padding,
			           menu_item_hight);

	for (int i=0;i<m_menu->GetItemCnt();i++)
	{
		DrawMenuItem(m_menu->GetItem(i),dc,rc);

		if(m_menu->GetItemType(i) ==auiItemSeparator )
			rc.y+=separator_height;
		else
			rc.y+=menu_item_hight;
	}
}

void auiPopupWindow::DrawMenuItem(auiMenuItem* item, wxDC& dc,wxRect& rect)
{
	if(item->item_type == auiItemSeparator)
	{
		dc.SetPen(wxPen(wxAuiUtils::GetInstance()->m_base_colour.ChangeLightness(80)));
		dc.DrawLine(rect.x,rect.y+(separator_height/2),rect.x+rect.width,rect.y+(separator_height/2));
		return;
	}

	//background
    if(item->IsSelect())
    {
//		dc.SetPen(wxPen(wxAuiUtils::GetInstance()->m_highlightColour));
//		dc.SetBrush(wxBrush(wxAuiUtils::GetInstance()->m_highlightColour));
//		dc.DrawRectangle(rect);
    	wxAuiUtils::GetInstance()->DrawMenuHighlight(dc,rect);
	}

    //checked mark
    if(item->item_type == auiItemCheck
     &&item->IsChecked())
    {
    	char check_mark[7][7]={
    		{0,0,0,0,0,0,1},
    		{0,0,0,0,0,1,1},
    		{1,0,0,0,1,1,1},
    		{1,1,0,1,1,1,0},
    		{1,1,1,1,1,0,0},
    		{0,1,1,1,0,0,0},
    		{0,0,1,0,0,0,0},
    	};

		if(item->IsSelect())
			dc.SetPen(wxPen(wxAuiUtils::GetInstance()->m_highlightTextColour));
		else
			dc.SetPen(wxPen(wxColor(0,0,0)));


		int x = rect.x+3;
		int y = rect.y+menu_item_hight/2-2;

		for(int i=0;i<7;i++)
			for(int j=0;j<7;j++)
		{
			if(check_mark[i][j])
				dc.DrawPoint(x+j,y+i);
		}

	}
    else if(item->item_type == auiItemRadio
     &&item->IsChecked())
    {
    	char check_mark[7][7]={
    		{0,0,0,0,0,0,0},
    		{0,0,1,1,1,0,0},
    		{0,1,1,1,1,1,0},
    		{0,1,1,1,1,1,0},
    		{0,1,1,1,1,1,0},
    		{0,0,1,1,1,0,0},
    		{0,0,0,0,0,0,0},
    	};

		if(item->IsSelect())
			dc.SetPen(wxPen(wxAuiUtils::GetInstance()->m_highlightTextColour));
		else
			dc.SetPen(wxPen(wxColor(0,0,0)));


		int x = rect.x+3;
		int y = rect.y+menu_item_hight/2-2;

		for(int i=0;i<7;i++)
			for(int j=0;j<7;j++)
		{
			if(check_mark[i][j])
				dc.DrawPoint(x+j,y+i);
		}

	}
    else if(  (item->item_type == auiItemNormal|| item->item_type == auiItemSubmenu)
            && item->item_has_bitmap)
    {
    	int bx = 2,by;
    	int h = item->item_bitmap.GetHeight();
    	by = (menu_item_hight - h)/2;
    	dc.DrawBitmap(item->item_bitmap,rect.x+bx,rect.y+by,true);
    }

    //text
    if(item->IsSelect())
    {
    	dc.SetTextForeground(wxAuiUtils::GetInstance()->m_highlightTextColour);
    }
    else
    {
    	dc.SetTextForeground(wxColor(0,0,0));
    }
	dc.DrawText(item->item_text,rect.x+l_padding,rect.y+t_padding);

	//sub menu arrow
	if(item->item_type == auiItemSubmenu)
	{
		if(item->IsSelect())
			dc.SetPen(wxPen(wxAuiUtils::GetInstance()->m_highlightTextColour));
		else
			dc.SetPen(wxPen(wxColor(0,0,0)));


		int x = rect.x+rect.width-4;
		int y = rect.y+rect.height/2;
		int h = 1;
		for(int i=0;i<4;i++)
		{
			dc.DrawLine(x,y,x,y+h);
			x --;
			y --;
			h +=2;
		}

	}
}

void auiPopupWindow::DrawArrow(wxDC& dc,wxRect& rect)
{
	int x = rect.x+rect.width-2;
	int y = rect.y+rect.height/2;
	int h = 1;
	for(int i=0;i<4;i++)
	{
		dc.DrawLine(x,y,x,y+h);
		x --;
		y --;
		h +=2;
	}
}

void auiPopupWindow::Dismiss()
{
	if(HasCapture())
	    ReleaseMouse();
	Hide();
}


#ifdef __WXMSW__
#include <windows.h>
#endif

void auiPopupWindow::OnRightDown(wxMouseEvent& event)
{
    wxPoint pos = event.GetPosition();

    wxWindow *win = (wxWindow *)event.GetEventObject();
    int hitres =  win->HitTest(pos.x, pos.y) ;

    switch (hitres)
    {
        case wxHT_WINDOW_INSIDE:

        	break;

        case wxHT_WINDOW_OUTSIDE:
            {
            	pos = ClientToScreen(pos);

            	if(!m_menu->R_ClickOnParent(pos))
            	{
            		m_menu->Dismiss();
		    		m_menu->ResetParent();

					wxWindow *winUnder = wxFindWindowAtPoint(pos);
					if ( winUnder )
					{
#ifdef __WXMSW__
						pos = winUnder->ScreenToClient(pos);
						long lParam = pos.y;
						lParam <<= 16;
						lParam += pos.x;
						winUnder->MSWWindowProc(WM_RBUTTONDOWN,0,lParam);
#else

		                wxMouseEvent event2(event);
		                ClientToScreen(&event2.m_x, &event2.m_y);
	                    winUnder->ScreenToClient(&event2.m_x, &event2.m_y);

	                    event2.SetEventObject(winUnder);
	                    wxPostEvent(winUnder->GetEventHandler(), event2);
#endif

					}
					else
					{
						wxFrame* f = m_menu->menu_bar->m_frame;
						wxRect rc = f->GetRect();
						if(rc.Contains(pos))
						{
							mouse_event(MOUSEEVENTF_RIGHTDOWN,pos.x,pos.y,0,NULL);
						}
					}

            	}
            }
            break;

        default:
            event.Skip();
            break;
    }
}

void auiPopupWindow::OnLeftUp(wxMouseEvent& event)
{
    wxPoint pos = event.GetPosition();

    wxWindow *win = (wxWindow *)event.GetEventObject();
    int hitres =  win->HitTest(pos.x, pos.y) ;

    switch (hitres)
    {
        case wxHT_WINDOW_INSIDE:
        	ProcessLeftUp(pos);
        	break;

        case wxHT_WINDOW_OUTSIDE:
            {
            	pos = ClientToScreen(pos);

            	if(!m_menu->R_ClickOnParent(pos))
            	{
            		m_menu->Dismiss();
		    		m_menu->ResetParent();
            	}
            }
            break;

        default:
            event.Skip();
            break;
    }
}



void auiPopupWindow::OnLeftDown(wxMouseEvent& event)
{
    wxPoint pos = event.GetPosition();

    wxWindow *win = (wxWindow *)event.GetEventObject();
    int hitres =  win->HitTest(pos.x, pos.y) ;

    switch (hitres)
    {
        case wxHT_WINDOW_INSIDE:
        	ProcessLeftDown(pos);
        	break;

        case wxHT_WINDOW_OUTSIDE:
            {
            	pos = ClientToScreen(pos);

            	if(!m_menu->L_ClickOnParent(pos))
            	{
            		m_menu->Dismiss();
		    		m_menu->ResetParent();

					wxWindow *winUnder = wxFindWindowAtPoint(pos);
					if ( winUnder )
					{
#ifdef __WXMSW__
						pos = winUnder->ScreenToClient(pos);
						long lParam = pos.y;
						lParam <<= 16;
						lParam += pos.x;
						winUnder->MSWWindowProc(WM_LBUTTONDOWN,0,lParam);
#else

		                wxMouseEvent event2(event);
		                ClientToScreen(&event2.m_x, &event2.m_y);
	                    winUnder->ScreenToClient(&event2.m_x, &event2.m_y);

	                    event2.SetEventObject(winUnder);
	                    wxPostEvent(winUnder->GetEventHandler(), event2);
#endif

					}
					else
					{
						wxFrame* f = m_menu->menu_bar->m_frame;
						wxRect rc = f->GetRect();
						if(rc.Contains(pos))
						{
							mouse_event(MOUSEEVENTF_LEFTDOWN,pos.x,pos.y,0,NULL);
						}
					}

            	}
            }
            break;

        default:
            event.Skip();
            break;
    }
}

void auiPopupWindow::OnMouseMove(wxMouseEvent& event)
{

    wxPoint pos = event.GetPosition();
    if(   (oldMousePos.x==-100&&oldMousePos.y==-100)
    	||(oldMousePos == pos)                        )
    {
    	oldMousePos = pos;
    	return;
    }

    oldMousePos = pos;

    wxWindow *win = (wxWindow *)event.GetEventObject();
    int hitres =  win->HitTest(pos.x, pos.y) ;

    switch (hitres)
    {
        case wxHT_WINDOW_INSIDE:
        	ProcesMouseMove(pos);
        	break;

        case wxHT_WINDOW_OUTSIDE:
            {
            	pos = ClientToScreen(pos);

            	if(!m_menu->MoveOnParent(pos))
            	{

            	}
            }
            break;

        default:
            event.Skip();
            break;
    }
}

void auiPopupWindow::ProcesMouseMove(const wxPoint& pos)
{
	const wxPoint pt  = pos;
    int last_active_item = m_menu->GetSelectItem();
	int item = GetItemByPosition(pt);
	if(item == last_active_item)
	{
		return;
	}

	m_menu->UnSelectItem();
	m_menu->DismissSubMenu();

	if(-1!=item)
	{
		m_menu->SelectItem(item);
		auiMenuItem* cItem = m_menu->GetItem(item);
		if( cItem->item_type == auiItemSubmenu
		&& !cItem->IsSubmenuOnShow())
		{
			wxPoint pts = ClientToScreen(pt);
			wxRect rc = GetItemRect(item);
			rc.x += (pts.x-pt.x);
			rc.y += (pts.y-pt.y);
			cItem->sub_menu->m_popupMenu->SubMenuAdjustPosition(rc);
			cItem->sub_menu->Popup(m_menu->menu_bar);
			cItem->SetSubmenuOnShow(true);
		}
	}
	Refresh(false);
}

void auiPopupWindow::ProcessLeftDown(const wxPoint& pt)
{
	return;
}

void auiPopupWindow::ProcessLeftUp(const wxPoint& pt)
{

	int item = GetItemByPosition(pt);

	if(item==-1)
	{
		return;
	}

	auiMenuItem* cItem = m_menu->GetItem(item);

	if(cItem->item_type == auiItemSubmenu)
	{
		if(cItem->IsSubmenuOnShow()==false)
		{
			wxPoint pts = ClientToScreen(pt);
			wxRect rc = GetItemRect(item);
			rc.x += (pts.x-pt.x);
			rc.y += (pts.y-pt.y);
			cItem->sub_menu->m_popupMenu->SubMenuAdjustPosition(rc);
			cItem->sub_menu->Popup(m_menu->menu_bar);
			cItem->SetSubmenuOnShow(true);
		}
		else
		{
			int scnt = cItem->sub_menu->GetItemCnt();
			for(int i=0;i<scnt;i++)
			{
				auiMenuItem* sItem = cItem->sub_menu->GetItem(i);
				if(sItem->item_type == auiItemSubmenu
				  &&sItem->IsSubmenuOnShow())
				{
					sItem->sub_menu->Dismiss();
					sItem->SetSubmenuOnShow(false);
					sItem->SetSelect(false);
				}
			}
			cItem->sub_menu->UnSelectItem();
			cItem->sub_menu->m_popupMenu->Refresh(false);
		}

		return ;
	}
	else if(cItem->item_type == auiItemCheck)
	{
		if(cItem->IsChecked())
			cItem->SetChecked(false);
		else
			cItem->SetChecked(true);

	}
	else if(cItem->item_type == auiItemRadio)
	{
		int cur_group_id = cItem->item_group;

		for(int i=0;i<m_menu->GetItemCnt();i++)
		{
			if(m_menu->GetItem(i)->item_group==cur_group_id)
				m_menu->GetItem(i)->SetChecked(false);
		}

		cItem->SetChecked(true);

	}

	wxNotifyEvent e(wxEVT_COMMAND_MENU_SELECTED, cItem->item_id);
	GetEventHandler()->ProcessEvent(e);

	m_menu->Dismiss();
	m_menu->ResetParent();


}

#include "wx/msw/private.h"

void auiPopupWindow::Popup(wxWindow *focus,bool selectFirst)
{
    if(selectFirst)
    {
    	m_menu->SelectItem(0);
    }

	oldMousePos.x = -100;
	oldMousePos.y = -100;

	Show();

    if ( !HasCapture() )
    {
    	CaptureMouse();
    }

#ifdef __WXMSW__
    // ensure that this window is really on top of everything: without using
    // SetWindowPos() it can be covered by its parent menu which is not
    // really what we want
    wxWindowList popList;
    popList.Append(this);
	auiMenu *menuParent = m_menu->m_parent;
	while ( menuParent )
	{
		popList.Append(menuParent->m_popupMenu);
		menuParent= menuParent->m_parent;
	}

	wxWindowListNode *f=popList.GetFirst();
	wxWindowListNode *n=f->GetNext();
    while ( n )
    {

        if ( !::SetWindowPos(GetHwndOf(n->GetData()), GetHwndOf(f->GetData()),
                             0, 0, 0, 0,
                             SWP_NOMOVE | SWP_NOSIZE | SWP_NOREDRAW) )
        {

        }

        f=n;
        n=n->GetNext();

    }

    Refresh(false);
#endif // __WXMSW__

    focus->SetFocus();

}

void auiPopupWindow::OnCaptureLost(wxMouseCaptureLostEvent& WXUNUSED(event))
{
	m_menu->Dismiss();
	m_menu->ResetParent();
}


void auiPopupWindow::MainMenuAdjustPosition(const wxPoint& ptOrigin, int menuBarHight)
{
    // determine the position and size of the screen we clamp the popup to
    wxPoint posScreen;
    wxSize sizeScreen;

    const int displayNum = wxDisplay::GetFromPoint(ptOrigin);
    if ( displayNum != wxNOT_FOUND )
    {
        const wxRect rectScreen = wxDisplay(displayNum).GetGeometry();
        posScreen = rectScreen.GetPosition();
        sizeScreen = rectScreen.GetSize();
    }
    else // outside of any display?
    {
        // just use the primary one then
        posScreen = wxPoint(0, 0);
        sizeScreen = wxGetDisplaySize();
    }

    const wxSize sizeSelf = GetSize();

    // is there enough space to put the popup below the window (where we put it
    // by default)?
    wxCoord y = ptOrigin.y;
    if ( y + sizeSelf.y > posScreen.y + sizeScreen.y )
    {
        // check if there is enough space above
        if ( ptOrigin.y > sizeSelf.y+menuBarHight )
        {
            // do position the control above the window
            y -= (sizeSelf.y+menuBarHight);
        }

    }

    // now check left/right too
    wxCoord x = ptOrigin.x;
    wxCoord overLeft =  (x + sizeSelf.x) - (posScreen.x + sizeScreen.x);
    if(overLeft>0)
    {
    	x -= overLeft;
    }
    if(x<0)
    {
    	x = 0;
    }
    Move(x, y, wxSIZE_NO_ADJUSTMENTS);
}

void auiPopupWindow::SubMenuAdjustPosition(const wxRect& rect)
{
    // determine the position and size of the screen we clamp the popup to
    wxPoint posScreen;
    wxSize sizeScreen;

    wxPoint ptOrigin = wxPoint(rect.x+rect.width-2,rect.y);

    const int displayNum = wxDisplay::GetFromPoint(ptOrigin);
    if ( displayNum != wxNOT_FOUND )
    {
        const wxRect rectScreen = wxDisplay(displayNum).GetGeometry();
        posScreen = rectScreen.GetPosition();
        sizeScreen = rectScreen.GetSize();
    }
    else // outside of any display?
    {
        // just use the primary one then
        posScreen = wxPoint(0, 0);
        sizeScreen = wxGetDisplaySize();
    }

    const wxSize sizeSelf = GetSize();

    // is there enough space to put the popup below the window (where we put it
    // by default)?
    wxCoord y = ptOrigin.y-t_margin-1;
    if ( y + sizeSelf.y > posScreen.y + sizeScreen.y )
    {
        // check if there is enough space above
        if ( ptOrigin.y > sizeSelf.y )
        {
            // do position the control above the window
            y -= (sizeSelf.y);
        }

    }

    // now check left/right too
    wxCoord x = ptOrigin.x;
    wxCoord overLeft =  (x + sizeSelf.x) - (posScreen.x + sizeScreen.x);
    if(overLeft>0)
    {
    	x -= (sizeSelf.x+rect.width);
    }
    if(x<0)
    {
    	x = 0;
    }
    Move(x, y, wxSIZE_NO_ADJUSTMENTS);
}

int auiPopupWindow::GetItemByPosition(const wxPoint& pt)
{

    if(pt.y<(1+t_margin))
    	return -1;

    if(pt.x<(1+l_margin)
    || pt.x>(1+l_margin+l_padding+menu_item_width+r_padding))
    {
    	return -1;
    }

    int begin = 1+t_margin;

    for(int i=0;i<m_menu->GetItemCnt();i++)
    {
    	int item_height = menu_item_hight;
    	if(m_menu->GetItemType(i)==auiItemSeparator)
    		item_height = separator_height;

    	if(pt.y>begin && pt.y<(begin+item_height))
    	{
    		if(m_menu->GetItemType(i)==auiItemSeparator)
    		    return -1;
    		else
    			return i;
    	}

    	begin += item_height;

    }

    return -1;
}


wxRect auiPopupWindow::GetItemRect(int index)
{
	wxRect rc = wxRect(0,0,0,0) ;

	if(index<0||index>=m_menu->GetItemCnt())
		return rc;

	rc = wxRect(1+l_margin,
			           1+t_margin,
			           l_padding+menu_item_width+r_padding,
			           menu_item_hight);

	for (int i=0;i<index;i++)
	{
		if(m_menu->GetItemType(i) ==auiItemSeparator )
			rc.y+=separator_height;
		else
			rc.y+=menu_item_hight;
	}

	return rc;
}
